package com.denlaku.longan.service.impl;

import com.denlaku.longan.datac.SqlReturn;
import com.denlaku.longan.qo.Query;
import com.denlaku.longan.qo.RefreshQuery;
import com.denlaku.longan.service.DataFetchService;
import com.denlaku.longan.service.DataSetService;
import com.denlaku.longan.service.RefreshService;
import com.denlaku.longan.sql.GroupByItem;
import com.denlaku.longan.sql.Limit;
import com.denlaku.longan.sql.OrderByItem;
import com.denlaku.longan.sql.SelectItem;
import com.denlaku.longan.sql.SqlInfo;
import com.denlaku.longan.sql.WhereItem;
import com.denlaku.longan.util.ForkJoinUtil;
import com.denlaku.longan.util.Lists;
import com.denlaku.longan.util.Objects;
import com.denlaku.longan.vo.Component;
import com.denlaku.longan.vo.ComponentData;
import com.denlaku.longan.vo.Condition;
import com.denlaku.longan.vo.DataSet;
import com.denlaku.longan.vo.Dimension;
import com.denlaku.longan.vo.Measure;
import com.denlaku.longan.vo.Order;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Supplier;

/**
 * 组件数据刷新服务
 *
 * @author tianx
 */
@Service
@Slf4j
public class RefreshServiceImpl implements RefreshService {
    @Autowired
    private DataFetchService dataFetchService;

    @Autowired
    private DataSetService dataSetService;

    @Override
    public ComponentData refresh(RefreshQuery query) {
        trimQuery(query);
        Component component = query.getComponent();
        ComponentData data = new ComponentData();
        if (component.getDataSetId() == null) {
            return data;
        }
        SqlInfo info = initSqlInfo(query);

        SqlReturn sqlReturn = dataFetchService.query(info);

        data.setStatus(sqlReturn.isStatus());
        data.setId(component.getId());
        data.setRows(sqlReturn.getRows());
        if (query.isDebug()) {
            data.setSql(sqlReturn.getSql());
            data.setParams(sqlReturn.getParams());
            data.addDebug(sqlReturn.getThrowable());
        }

        return data;
    }

    private SqlInfo initSqlInfo(RefreshQuery query) {
        SqlInfo info = new SqlInfo();
        info.setTableName(newTableName(query.getDataSet()));
        info.setDataSet(query.getDataSet());
        Integer pageNum = query.getPageNum();
        Integer pageSize = query.getPageSize();
        if (Objects.isNonNull(pageNum, pageSize)) {
            Limit limit = new Limit();
            int start = (pageNum - 1) * pageSize;
            limit.setStart(start);
            limit.setSize(pageSize);
            info.setLimit(limit);
        }
        Component component = query.getComponent();
        // 维度
        List<Dimension> dimensions = ListUtils.emptyIfNull(component.getDimensions());
        dimensions.forEach(dimension -> processDimension(info, dimension));
        // 度量
        List<Measure> measures = ListUtils.emptyIfNull(component.getMeasures());
        measures.forEach(measure -> processMeasure(info, measure));
        // 过滤器
        List<Condition> filters = ListUtils.emptyIfNull(component.getFilters());
        filters.forEach(filter -> processFilter(info, filter));
        return info;
    }

    private void processFilter(SqlInfo info, Condition filter) {
        List<Object> values = filter.getValues();
        if (CollectionUtils.isEmpty(values)) {
            return;
        }
        String operator = filter.getOperator();
        WhereItem item = new WhereItem();
        item.setOperator(operator);
        item.setValues(values);
        item.setName(filter.getName());
        info.addWhereItem(item);
    }

    private void processMeasure(SqlInfo info, Measure measure) {
        String aggregator = measure.getAggregator();
        measure.setAggregator(aggregator);
        SelectItem selectItem = newSelectItem(measure);
        info.addSelectItem(selectItem);
        Order order = measure.getOrder();
        OrderByItem orderByItem = newOrderByItem(order);
        info.addOrderByItem(orderByItem);
        if (StringUtils.isEmpty(aggregator)) {
            GroupByItem groupByItem = newGroupByItem(measure);
            info.addGroupByItem(groupByItem);
        }
    }

    private void processDimension(SqlInfo info, Dimension dimension) {
        SelectItem selectItem = newSelectItem(dimension);
        info.addSelectItem(selectItem);
        Order order = dimension.getOrder();
        OrderByItem orderByItem = newOrderByItem(order);
        info.addOrderByItem(orderByItem);
        GroupByItem groupByItem = newGroupByItem(dimension);
        info.addGroupByItem(groupByItem);
    }

    @Override
    public List<ComponentData> refreshes(List<RefreshQuery> queries, boolean debug) {
        int size = queries.size();
        if (size == 1) {
            RefreshQuery query = queries.getFirst();
            query.setDebug(debug);
            ComponentData refresh = this.refresh(query);
            return Lists.of(refresh);
        }
        try {
            List<Supplier<ComponentData>> suppliers = Lists.ofSize(size);
            queries.stream().peek(query -> query.setDebug(debug))
                    .forEach(query -> suppliers.add(() -> refresh(query)));
            return ForkJoinUtil.supplyList(suppliers);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return Lists.empty();
    }

    private void trimQuery(RefreshQuery query) {
        Component component = query.getComponent();
        Long dataSetId = component.getDataSetId();
        if (dataSetId != null) {
            DataSet dataSet = dataSetService.getCache(Query.of(dataSetId));
            query.setDataSet(dataSet);
        }
    }

    private GroupByItem newGroupByItem(Dimension dimension) {
        GroupByItem item = new GroupByItem();
        item.setName(dimension.getName());
        return item;
    }

    private GroupByItem newGroupByItem(Measure measure) {
        GroupByItem item = new GroupByItem();
        item.setName(measure.getName());
        return item;
    }

    private SelectItem newSelectItem(Dimension dimension) {
        SelectItem item = new SelectItem();
        item.setName(dimension.getName());
        return item;
    }

    private SelectItem newSelectItem(Measure measure) {
        SelectItem item = new SelectItem();
        item.setName(measure.getName());
        item.setAggregator(measure.getAggregator());
        return item;
    }

    private String newTableName(DataSet dataSet) {
        String command = dataSet.getCommand();
        return String.format("(%s) t_mid_165631313", command);
    }

    private OrderByItem newOrderByItem(Order order) {
        if (order == null) {
            return null;
        }
        OrderByItem item = new OrderByItem();
        item.setName(order.getName());
        item.setOrder(order.getPolicy());
        item.setValues(order.getValues());
        return item;
    }

}
